package com.hatricks.is.gld.work.ai;

import cn.hutool.core.date.StopWatch;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.dashscope.app.Application;
import com.alibaba.dashscope.app.ApplicationParam;
import com.alibaba.dashscope.app.ApplicationResult;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentStyle;
import com.alibaba.excel.annotation.write.style.HeadStyle;
import com.alibaba.excel.enums.poi.HorizontalAlignmentEnum;
import com.hatricks.is.excel.ExcelUtils;
import com.hatricks.is.excel.ExtractLdParams;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @packageName: com.hatricks.is.gdnwork.ai
 * @className: TyAiTest
 * @author: yanyuhui y568821795@outlook.com
 * @date: 2025-02-17 08:42
 * @description: 通义灵码的ai测试用例
 */
@Slf4j
public class BaiLianLargeModelTest {
    // chenc账号的workspaceId
    public static final String workspaceId = "llm-c76qrb2wkssy1zui";
    // 企业数据服务
    public static final String apiKey = "sk-617101bad3194f89bcc123ed080ac9e2";
    // 提取楼栋的单体id
    public static final String appId = "958623ddbe6e417a8109d60e1eb8adf3";

    public static final Map<String, String> CACHE_PROJECT = new HashMap<>();

    public static final String TEMPLATE_PROMOTE = "现有格式要求: {}  期望值为: {} \n 以上为示例格式 按照此格式输出即可\n ";

    @SneakyThrows
    public static void main(String[] args) {
        testExcel();
        //test1();
    }

    private static void test1() {
        for (int i = 0; i < 3; i++) {
            String params = "项目名称为“1”；工程名称为“青海-卫生建筑-门急诊医技综合楼”；单体名称为“青海-卫生建筑-门急诊医技综合楼”";
            ApplicationResult applicationResult = callLlmApplication(params);
            String text = applicationResult.getOutput().getText();
            if (StrUtil.length(text) > params.length()) {
                text = hallucinationReduction(params);
            }
            log.info("当前返回值:[{}]", text);
        }
    }

    private static String hallucinationReduction(String params) {
        log.warn("幻觉修复开始 [{}]", params);
        ApplicationResult applicationResult1 = callLlmApplication(params.concat(" 不需要回复多余内容,仅仅需要楼栋名称即可"));
        return applicationResult1.getOutput().getText();
    }

    private static void testExcel() {
        List<ExtractLdParams> extractLdParams = ExcelUtils.readData("C:\\work_space\\java_work_space\\public_utils\\25-2-楼栋名称优化.xlsx", ExtractLdParams.class);
        StopWatch sw = new StopWatch();
        sw.start();
        // 期望值, 目标值, 成功率
        int dataSize = extractLdParams.size();
        //int dataSize = 402;
        int successNums = 0;
        List<LdParams> ldParamsExcelLists = new LinkedList<>();
        int offset = 0;
        //int offset = 50;
        for (int i = offset; i < dataSize; i++) {
            ExtractLdParams extractLdParam = extractLdParams.get(i);
            try {
                String verifiedBuildingCode = extractLdParam.getVerifiedBuildingCode();
                if (StrUtil.isBlank(verifiedBuildingCode)) {
                    //log.info("数据异常,校验的楼栋名称为空");
                    successNums++;
                    continue;
                }

                // 进行数据测试使用
                LdParams ldParams = LdParams.from(extractLdParam.getProjectName(), extractLdParam.getEngineeringName(), extractLdParam.getBuildingName());
                String params = ldParams.buildLlmMessage();
                params = CACHE_PROJECT.getOrDefault(extractLdParam.getProjectName() + extractLdParam.getEngineeringName() + extractLdParam.getPreliminaryType(), StrUtil.EMPTY) + params;
                // 在此处进行幻觉修复
                ApplicationResult result = callLlmApplication(params);
                String responseText = LLMUtils.getJSONString(result.getOutput().getText());
                if (StrUtil.length(responseText) > params.length()) {
                    responseText = LLMUtils.getJSONString(hallucinationReduction(params));
                }

                if (!JSONUtil.isJson(responseText)) {
                    log.error("当前模型异常 = 参数为:[{}] 期望值为:[{}], 模型的值为:[{}]", params, verifiedBuildingCode, responseText);
                    continue;
                }


                JSONObject parse = JSONUtil.parseObj(responseText);
                responseText = parse.getStr("extractLdName");
                if (StrUtil.equalsIgnoreCase(responseText, verifiedBuildingCode)) {
                    successNums++;
                    ldParams.setSuccess("✅");
                    ldParams.setExpectValue(verifiedBuildingCode);
                    ldParams.setModelValue(responseText);

                    log.info("匹配成功, 参数为:[{}] 期望值为:[{}] 模型值为:[{}]", params, verifiedBuildingCode, responseText);
                } else {
                    ldParams.setSuccess("❌");
                    ldParams.setExpectValue(verifiedBuildingCode);
                    ldParams.setModelValue(responseText);
                    log.error("当前匹配失败 =参数为:[{}] 期望值为:[{}], 模型的值为:[{}]", params, verifiedBuildingCode, responseText);
                }

                String cacheKey = ldParams.getProjectName() + ldParams.getEngineeringName() + extractLdParam.getPreliminaryType();
                CACHE_PROJECT.putIfAbsent(cacheKey, StrUtil.format(TEMPLATE_PROMOTE, params, responseText));

                ldParamsExcelLists.add(ldParams);
                ThreadUtil.safeSleep(RandomUtil.randomLong(100, 2000));
            } catch (Exception e) {
                log.error("当前LLM调用发生异常", e);
            }
        }

        // 使用 EasyExcel 写入
        String pathName = "C:\\work_space\\java_work_space\\public_utils\\infrastructure-s\\src\\main\\java\\com\\hatricks\\is\\gdnwork\\ai\\验证.xlsx";
        if (FileUtil.exist(pathName)) {
            FileUtil.del(pathName);
        }

        EasyExcel.write(pathName, LdParams.class)
                .sheet("数据报表")
                .doWrite(ldParamsExcelLists);

        sw.stop();
        log.info("当前校验的成功率为:[{}]", NumberUtil.formatPercent(NumberUtil.div(successNums, (dataSize - offset)), 2));
        log.info("当前数据解析耗时:[{}]", sw.prettyPrint(TimeUnit.SECONDS));
    }

    @SneakyThrows
    private static ApplicationResult callLlmApplication(String params) {
        ApplicationParam param = ApplicationParam.builder()
                .workspace(workspaceId)
                .apiKey(apiKey)
                .appId(appId)
                .prompt(params)
                .build();
        return new Application().call(param);
    }


    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @HeadStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER) // 表头居中
    @ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER) // 内容居中
    private static class LdParams {
        /**
         * 项目名称
         */
        @ExcelProperty("项目名称")
        @ColumnWidth(15)
        private String projectName;
        /**
         * 工程名称
         */
        @ExcelProperty("工程名称")
        @ColumnWidth(50)
        private String engineeringName;
        /**
         * 楼栋名称
         */
        @ExcelProperty("楼栋名称")
        @ColumnWidth(30)
        private String ldName;
        @ExcelProperty("匹配状态")
        @ColumnWidth(15)
        private String success;
        @ExcelProperty("0221依据识别结果更新")
        @ColumnWidth(40)
        private String expectValue;
        @ExcelProperty("模型实际值")
        @ColumnWidth(40)
        private String modelValue;

        @ExcelProperty("类型初步判断")
        private String type;

        public LdParams(String projectName, String engineeringName, String ldName) {
            this.projectName = projectName;
            this.engineeringName = engineeringName;
            this.ldName = ldName;
        }

        public static LdParams from(String projectName, String modelName, String ldName) {
            return new LdParams(projectName, modelName, ldName);
        }

        public String buildLlmMessage() {
            return "项目名称为" + "“" + projectName + "”；" + "工程名称为" + "“" + engineeringName + "”；" + "单体名称为" + "“" + ldName + "”";
        }

    }
}
